﻿#ifndef ___PCLIB_S_NETUTIL___
#define ___PCLIB_S_NETUTIL___


#include "../m_core/m_core.hpp"




/**
*@brief     网卡信息
*/
typedef  struct _StNetAdapter
{
    char szName[MAX_PATH];  //网卡名称
    char szIp[MAX_PATH];    //网卡ip地址，网络未连接时或获取ip失败时ip为0.0.0.0
    char szMac[MAX_PATH];   //网卡mac地址，网卡获取失败时为空
    int  type;              //网卡类型，0 无线网卡 1 普通网卡 （目前只支持windows且不支持xp）
}StNetAdapter, *lpStNetAdapter;

/**
*@brief        网络工具类
*/
class CPCNetUtil : CPCNoCopyable
{
public:

    /**
    *@brief     获取本机的(多个)网卡信息。不包括loopback
    *@param     info    [out]    网卡信息结构。
    *@return    成功：CPCResult.Succ    失败：CPCResult.ErrDesc[错误描述]
    */
    static CPCResult<> PCGetNetAdapterInfo(std::vector<StNetAdapter> &vecInfo)
    {
        CPCResult<> result;
        vecInfo.clear();

#if defined(PCOS_WIN)
        //分配PIP_ADAPTER_INFO结构体空间
        PIP_ADAPTER_INFO pInfo = new IP_ADAPTER_INFO();
        if (NULL == pInfo)
        {
            return result.SetFail("new IP_ADAPTER_INFO fail.");
        }
        CPCAutoObj autoAdptInfo([&pInfo](){ PCDelete(pInfo); });

        unsigned long stSize = sizeof(IP_ADAPTER_INFO);
        int nRet = GetAdaptersInfo(pInfo, &stSize);
        if (ERROR_BUFFER_OVERFLOW == nRet)
        {
            delete pInfo;
            pInfo = (PIP_ADAPTER_INFO)new unsigned char[stSize];
            nRet = GetAdaptersInfo(pInfo, &stSize);
        }
        if (ERROR_SUCCESS != nRet)
        {
            return result.SetSystemFail();
        }

        //输出网卡信息
        for (PIP_ADAPTER_INFO pIpAdapterInfo = pInfo;
            pIpAdapterInfo != NULL;
            pIpAdapterInfo = pIpAdapterInfo->Next)
        {
            if (pIpAdapterInfo->Type == MIB_IF_TYPE_LOOPBACK)
                continue;

            StNetAdapter  adapterInfo;
            if(71  == pIpAdapterInfo->Type) //71 = IF_TYPE_IEEE80211 xp不一定有这个宏
            {
                adapterInfo.type = 0;
            }
            else
            {
                adapterInfo.type = 1;
            }
            memset(adapterInfo.szName, 0, sizeof(adapterInfo.szName));
            memset(adapterInfo.szIp, 0, sizeof(adapterInfo.szIp));
            memset(adapterInfo.szMac, 0, sizeof(adapterInfo.szMac));
            snprintf(adapterInfo.szName, sizeof(adapterInfo.szName), "%s%s", pIpAdapterInfo->AdapterName, pIpAdapterInfo->Description);
            snprintf(adapterInfo.szIp, sizeof(adapterInfo.szIp), "%s", pIpAdapterInfo->IpAddressList.IpAddress.String);
            char szAddrBit[8];
            for (unsigned int i = 0; i < pIpAdapterInfo->AddressLength; i++)
            {
                memset(szAddrBit, 0, sizeof(szAddrBit));
                snprintf(szAddrBit, sizeof(szAddrBit), "%02X-", (unsigned char)pIpAdapterInfo->Address[i]);
                strcat_s(adapterInfo.szMac, sizeof(adapterInfo.szMac), szAddrBit);
            }
            CPCStrUtil::PCRemoveLast(adapterInfo.szMac, 1);
            vecInfo.push_back(adapterInfo);
        }
#elif defined(PCOS_LINUX)
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd == INVALID_SOCKET)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoSocketFd([&fd](){PCCloseSocket(fd); });

        //name list
        std::set<std::string> m_namelist;

        //ioctl
        struct ifreq buf[MAX_PATH];
        struct ifconf ifc;
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = (caddr_t)buf;
        int ret = ioctl(fd, SIOCGIFCONF, (char*)&ifc);
        if (ret == 0)
        {
            //网口总数 ifc.ifc_len 应该是一个出入参数
            for (int i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++)
            {
                m_namelist.insert(buf[i].ifr_name);
            }
        }

        //proc/net/dev
        std::vector<std::string> devlst = CPCFileUtil::PCReadFileLines("/proc/net/dev");
        for (auto it = devlst.begin(); it != devlst.end(); ++it)
        {
            const char* pData = (*it).c_str();
            int start = -1, end = -1;
            for (size_t i = 0; i < (*it).length(); ++i)
            {
                if (start == -1)
                {
                    if (pData[i] != ' ' && pData[i] != '\t' && pData[i] != '\r')
                    {
                        start = i;
                    }
                    continue;
                }
                if (end == -1 && pData[i] == ':')
                {
                    end = i;
                    break;
                }
            }
            if (start == -1 || end == -1 || end - start <= 0)
                continue;
            m_namelist.insert(std::string(pData + start, end - start));
        }

        //get netif info
        struct ifreq ifr;
        for (auto it = m_namelist.begin(); it != m_namelist.end(); ++it)
        {
            //获取第i个网口信息 
            strncpy(ifr.ifr_name, (*it).c_str(), IFNAMSIZ);
            ret = ioctl(fd, SIOCGIFFLAGS, (char*)&ifr);
            if (ret != 0)
                continue;

            //获取网口状态
            if (ifr.ifr_flags & IFF_LOOPBACK)
                continue;
            if (!(ifr.ifr_flags & IFF_UP))
                continue;

            StNetAdapter  adapterInfo;
            adapterInfo.type = 1;   //TODO
            memset(adapterInfo.szName, 0, sizeof(adapterInfo.szName));
            memset(adapterInfo.szIp, 0, sizeof(adapterInfo.szIp));
            memset(adapterInfo.szMac, 0, sizeof(adapterInfo.szMac));
            snprintf(adapterInfo.szName, sizeof(adapterInfo.szName), "%s", (*it).c_str());

            //获取当前网卡的ip地址
            ret = ioctl(fd, SIOCGIFADDR, (char*)&ifr);
            struct sockaddr_in * pAddr = (struct sockaddr_in *) &ifr.ifr_addr;
            if (ret != 0 || NULL == PC_INET_NTOP(AF_INET, &pAddr->sin_addr, adapterInfo.szIp, sizeof(adapterInfo.szIp)))
            {
                strcpy(adapterInfo.szIp, "0.0.0.0");
            }

            //获取当前网卡的mac
            ret = ioctl(fd, SIOCGIFHWADDR, (char*)&ifr);
            if (ret == 0)
            {
                snprintf(adapterInfo.szMac, sizeof(adapterInfo.szMac), "%02x-%02x-%02x-%02x-%02x-%02x",
                    (unsigned char)ifr.ifr_hwaddr.sa_data[0],
                    (unsigned char)ifr.ifr_hwaddr.sa_data[1],
                    (unsigned char)ifr.ifr_hwaddr.sa_data[2],
                    (unsigned char)ifr.ifr_hwaddr.sa_data[3],
                    (unsigned char)ifr.ifr_hwaddr.sa_data[4],
                    (unsigned char)ifr.ifr_hwaddr.sa_data[5]);
            }
            vecInfo.push_back(adapterInfo);
        }
#else
        //macos
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd == INVALID_SOCKET)
        {
            return result.SetSystemFail();
        }
        CPCAutoObj autoSocketFd([&fd](){PCCloseSocket(fd); });

        struct ifaddrs *ifa_list, *ifa;
        if (getifaddrs(&ifa_list)<0)
            return result.SetSystemFail();

        struct ifreq ifr;
        for (ifa = ifa_list; ifa; ifa = ifa->ifa_next)
        {
            if (NULL == (*ifa).ifa_addr)
                continue;
            struct sockaddr_dl* dl = (struct sockaddr_dl*)ifa->ifa_addr;
            if (dl->sdl_family != AF_LINK || dl->sdl_type != IFT_ETHER)
                continue;

            //获取第i个网口信息
            strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
            int ret = ioctl(fd, SIOCGIFFLAGS, (char*)&ifr);
            if (ret != 0)
                continue;

            //获取网口状态
            if (ifr.ifr_flags & IFF_LOOPBACK)
                continue;
            if (!(ifr.ifr_flags & IFF_UP))
                continue;

            StNetAdapter  adapterInfo;
            adapterInfo.type = 1; // TODO
            memset(adapterInfo.szName, 0, sizeof(adapterInfo.szName));
            memset(adapterInfo.szIp, 0, sizeof(adapterInfo.szIp));
            memset(adapterInfo.szMac, 0, sizeof(adapterInfo.szMac));
            snprintf(adapterInfo.szName, sizeof(adapterInfo.szName), "%s", ifa->ifa_name);

            //获取当前网卡的ip地址
            ret = ioctl(fd, SIOCGIFADDR, (char*)&ifr);
            struct sockaddr_in* pAddr = (struct sockaddr_in *) &ifr.ifr_addr;
            if (ret != 0 || NULL == PC_INET_NTOP(AF_INET, &pAddr->sin_addr, adapterInfo.szIp, sizeof(adapterInfo.szIp)))
            {
                strcpy(adapterInfo.szIp, "0.0.0.0");
            }

            //获取当前网卡的mac
            unsigned char *addr = (unsigned char*)LLADDR(dl);
            snprintf(adapterInfo.szMac, sizeof(adapterInfo.szMac), "%02x-%02x-%02x-%02x-%02x-%02x",
                (unsigned char)addr[0],
                (unsigned char)addr[1],
                (unsigned char)addr[2],
                (unsigned char)addr[3],
                (unsigned char)addr[4],
                (unsigned char)addr[5]);
            vecInfo.push_back(adapterInfo);
        }
        freeifaddrs(ifa_list);
#endif
        //输出mac地址，按从小到大顺序
        std::sort(vecInfo.begin(), vecInfo.end(),
            [](const StNetAdapter& net1, const StNetAdapter& net2)->bool
        {
            if (strlen(net1.szMac) == 0 || strlen(net2.szMac) == 0)
            {
                return false;
            }
            return (strcmp(net1.szMac, net2.szMac) < 0);
        }
        );
        return result;
    }
};






#endif    //___PCLIB_S_NETUTIL___